package br.com.devteam.sgap.repository;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.Query;

import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public abstract class BasicDAOImpl implements BasicDAO, Serializable {

	private static final long serialVersionUID = -7334258539951877597L;

	private static Logger logger = Logger.getLogger(BasicDAOImpl.class);

	public <T> T findById(Class<T> entityClass, Serializable id) {
		return (T) getEntityManager().find(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public <T> List<T> findByNamedQuery(String namedQuery,
			Map<String, Object> namedParams) {
		try {
			logger.info("Procurando pela namedQuery " + namedQuery + " com "
					+ namedParams.size() + " parametros");
			Query query = getEntityManager().createNamedQuery(namedQuery);
			if (namedParams != null) {
				Entry<String, Object> mapEntry;
				for (Iterator<Entry<String, Object>> it = namedParams
						.entrySet().iterator(); it.hasNext(); query
						.setParameter(mapEntry.getKey(), mapEntry.getValue())) {
					mapEntry = it.next();
					logger.info("Param: " + mapEntry.getKey() + ", Value: "
							+ mapEntry.getValue());
				}
			}
			List<T> returnList = (List<T>) query.getResultList();
			logger.info("Objetos Encontrados: " + returnList.size());
			return returnList;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao executar o findByNamedQuery com parâmetros. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException(
					"Ocorreu um erro ao executar o findByNamedQuery com parâmetros");
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public <T> List<T> findByNamedQuery(String namedQuery,
			Map<String, Object> namedParams, int maxResults) {
		try {
			logger.info("Procurando pela namedQuery " + namedQuery + " com "
					+ namedParams.size() + " parametros");
			Query query = getEntityManager().createNamedQuery(namedQuery);
			query.setMaxResults(maxResults);
			if (namedParams != null) {
				Entry<String, Object> mapEntry;
				for (Iterator<Entry<String, Object>> it = namedParams
						.entrySet().iterator(); it.hasNext(); query
						.setParameter(mapEntry.getKey(), mapEntry.getValue())) {
					mapEntry = it.next();
					logger.info("Param: " + mapEntry.getKey() + ", Value: "
							+ mapEntry.getValue());
				}
			}
			List<T> returnList = (List<T>) query.getResultList();
			logger.info("Objetos Encontrados: " + returnList.size());
			return returnList;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao executar o findByNamedQuery com parâmetros. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException(
					"Ocorreu um erro ao executar o findByNamedQuery com parâmetros");
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public <T> List<T> findByNamedQuery(String namedQuery) {
		try {
			logger.info((new StringBuilder("Procurando pela namedQuery "))
					.append(namedQuery).append(" sem nenhum parametro")
					.toString());
			Query query = getEntityManager().createNamedQuery(namedQuery);
			List<T> returnList = (List<T>) query.getResultList();
			logger.info("Objetos Encontrados: " + returnList.size());
			return returnList;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao executar o findByNamedQuery sem parâmetros. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException(
					"Ocorreu um erro ao executar o findByNamedQuery sem parâmetros");
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public <T> List<T> findByNamedQuery(String namedQuery, int maxResults) {
		try {
			logger.info((new StringBuilder("Procurando pela namedQuery "))
					.append(namedQuery).append(" sem nenhum parametro")
					.toString());
			Query query = getEntityManager().createNamedQuery(namedQuery);
			query.setMaxResults(maxResults);
			List<T> returnList = (List<T>) query.getResultList();
			logger.info("Objetos Encontrados: " + returnList.size());
			return returnList;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao executar o findByNamedQuery sem parâmetros. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException(
					"Ocorreu um erro ao executar o findByNamedQuery sem parâmetros");
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public <T> List<T> findByQuery(String hql, Map<String, Object> namedParams) {
		try {
			logger.info((new StringBuilder("Procurando pela query: ")).append(
					hql).toString());
			Query query = getEntityManager().createQuery(hql);
			if (namedParams != null) {
				Entry<String, Object> mapEntry;
				for (Iterator<Entry<String, Object>> it = namedParams
						.entrySet().iterator(); it.hasNext(); query
						.setParameter(mapEntry.getKey(), mapEntry.getValue())) {
					mapEntry = it.next();
					logger.info("Param: " + mapEntry.getKey() + ", Value: "
							+ mapEntry.getValue());
				}
			}
			List<T> returnList = (List<T>) query.getResultList();
			logger.info("Objetos Encontrados: " + returnList.size());
			return returnList;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao executar o findByQuery. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException("Ocorreu um erro ao executar o findByQuery");
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public <T> List<T> findByQuery(String hql, Map<String, Object> namedParams,
			int maxResults) throws DAOException {
		try {
			logger.info((new StringBuilder("Procurando pela query: ")).append(
					hql).toString());
			Query query = getEntityManager().createQuery(hql);
			query.setMaxResults(maxResults);
			if (namedParams != null) {
				Entry<String, Object> mapEntry;
				for (Iterator<Entry<String, Object>> it = namedParams
						.entrySet().iterator(); it.hasNext(); query
						.setParameter(mapEntry.getKey(), mapEntry.getValue())) {
					mapEntry = it.next();
					logger.info("Param: " + mapEntry.getKey() + ", Value: "
							+ mapEntry.getValue());
				}
			}
			List<T> returnList = (List<T>) query.getResultList();
			logger.info("Objetos Encontrados: " + returnList.size());
			return returnList;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao executar o findByQuery. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException("Ocorreu um erro ao executar o findByQuery");
		}
	}

	@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
	public <T> T save(T bean) {
		try {
			logger.info("Salvando Bean " + bean.getClass().getName());
			getEntityManager().persist(bean);
			return bean;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao tentar salvar. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException("Ocorreu um erro ao tentar salvar");
		}
	}

	@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
	public <T> T saveFlushAndClear(T bean) {
		try {
			logger.info("Salvando Bean " + bean.getClass().getName());
			getEntityManager().persist(bean);
			getEntityManager().flush();
			getEntityManager().clear();
			return bean;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao tentar salvar. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException("Ocorreu um erro ao tentar salvar");
		}
	}

	@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
	public <T> void update(T bean) {
		try {
			logger.info("Alterando Bean " + bean.getClass().getName());
			getEntityManager().merge(bean);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao tentar atualizar. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException("Ocorreu um erro ao tentar atualizar");
		}
	}

	@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
	public <T> void delete(T bean) {
		try {
			logger.info("Deletando Bean " + bean.getClass().getName());
			getEntityManager().remove(bean);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Ocorreu um erro ao tentar deletar. MSG ORIGINAL: "
					+ e.getMessage());
			throw new DAOException("Ocorreu um erro ao tentar deletar");
		}
	}

	public static Logger getLogger() {
		return logger;
	}
}
